﻿using Google.Apis.Tasks.v1.Data;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace G_Tasks
{
    public partial class TasksTabPage : TabPage
    {
        private readonly List<Task> deletedNotes = new List<Task>();
        private readonly TaskList taskList;
        private readonly object sync = new object();
        private readonly TasksListView tasksListView = new TasksListView();
        private ContextMenu taskMenu;

        /// <summary>
        /// Nový tab pro TODO list
        /// </summary>
        /// <param name="taskList"></param>
        public TasksTabPage(TaskList taskList)
        {
            InitializeComponent();

            taskMenu = new ContextMenu(new MenuItem[] { new MenuItem("Add", this.AddTask_Click), new MenuItem("Edit", this.EditTask_Click), new MenuItem("Remove", this.RemoveTask_Click), new MenuItem("-"), new MenuItem("sczdavos", this.Sczdavos_Click) });

            tasksListView.Dock = DockStyle.Fill;
            tasksListView.CheckBoxes = true;
            tasksListView.View = View.Details;
            tasksListView.ListViewItemSorter = new TasksComparer();
            tasksListView.HeaderStyle = ColumnHeaderStyle.None;
            tasksListView.Columns.Add(new ColumnHeader() { TextAlign = HorizontalAlignment.Left });
            tasksListView.Columns.Add(new ColumnHeader() { Width = 70, TextAlign = HorizontalAlignment.Right });
            tasksListView.ContextMenu = taskMenu;
            tasksListView.MultiSelect = false;
            tasksListView.ShowItemToolTips = true;
            tasksListView.FullRowSelect = true;

            this.Controls.Add(tasksListView);

            this.taskList = taskList;

            // Set the title.
            Text = taskList.Title;
            
            // Load all notes:   
            tasksListView.BeginUpdate();
            foreach (Task task in Program.Service.Tasks.List(taskList.Id).Fetch().Items)
            {
                AddNote(new NoteItem(task));
            }
            tasksListView.EndUpdate();

            // synchronizování dat
            Timer timer = new Timer();
            timer.Interval = 1000 * 60 * 5; // 5 min
            timer.Tick += (timerSender, timerArgs) => ClientSync();
            timer.Start();

            tasksListView.ItemChecked += tasksView_ItemChecked;
            tasksListView.MouseClick += tasksView_MouseClick;
            this.Resize += TasksTabPage_Resize;
        }

        /// <summary>
        /// Přizpůsobování velikosti sloupce při změně velikosti okna
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void TasksTabPage_Resize(object sender, EventArgs e)
        {
            tasksListView.Columns[0].Width = this.Width - 92;
        }

        void AddTask_Click(object sender, EventArgs e)
        {
            AddTaskForm addTask = new AddTaskForm(this);
            addTask.ShowDialog();
        }

        void EditTask_Click(object sender, EventArgs e)
        {
            if (tasksListView.SelectedItems.Count == 0) return;

            TasksListViewItem taskItem = (tasksListView.Items[tasksListView.SelectedItems[0].Name] as TasksListViewItem);
            NoteItem note = taskItem.note;
            AddTaskForm editTask = new AddTaskForm(this, note);
            editTask.ShowDialog();
        }

        void RemoveTask_Click(object sender, EventArgs e)
        {
            if (tasksListView.SelectedItems.Count == 0) return;

            NoteItem note = (tasksListView.SelectedItems[0] as TasksListViewItem).note;
            RemoveNote(note.RelatedTask);
        }

        void Sczdavos_Click(object sender, EventArgs e)
        {
            MessageBox.Show("Developed by: sczdavos [David Jančík], sczdavos@gmail.com");
        }

        /// <summary>
        /// Kliknu do prostoru položky - vybere se
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void tasksView_MouseClick(object sender, MouseEventArgs e)
        {
            foreach (ListViewItem item in tasksListView.SelectedItems)
                item.Selected = false;

            ListViewItem task = tasksListView.GetItemAt(e.X, e.Y);
            if (task != null)
                task.Selected = true;
        }

        void tasksView_ItemChecked(object sender, ItemCheckedEventArgs e)
        {
            if (!e.Item.Selected) return;

            NoteItem note = (e.Item as TasksListViewItem).note;
            if (note.NoteFinished != e.Item.Checked)
            {
                note.NoteFinished = e.Item.Checked;
                note.SubmitChanges();
                ClientSync();
            }
        }

        /// <summary>
        /// Přidá úkol
        /// </summary>
        /// <param name="note"></param>
        /// <returns></returns>
        public void AddNote(NoteItem note)
        {
            // Insert the new control as the first element, as it will be displayed on the bottom.
            SuspendLayout();
            TasksListViewItem taskItem = new TasksListViewItem(note);
            taskItem.Checked = taskItem.note.NoteFinished;
            taskItem.SubItems.Add(taskItem.note.DueToDateString());
            taskItem.ToolTipText = taskItem.note.NoteText;
            tasksListView.Items.Add(taskItem);
            tasksListView.Sort();
            ResumeLayout();
        }

        /// <summary>
        /// Deletes a note from the note list. - nečekaně
        /// </summary>
        public void RemoveNote(Task task)
        {
            tasksListView.Items.RemoveByKey(task.Id);
            deletedNotes.Add(task);
        }

        /// <summary>
        /// Synchronizes this client with the remote server.
        /// </summary>
        public void ClientSync(bool fetchNewData = true)
        {
            if (tasksListView.InvokeRequired)
            { 
                tasksListView.Invoke(new MethodInvoker(() => ClientSync(fetchNewData)));
                return;
            }
            
            lock (sync)
            {
                try
                {
                    var requests = new List<Action>();

                    // Add changes/inserts.
                    foreach (TasksListViewItem t in tasksListView.Items)
                    {
                        if (t.note.Modified)
                        {
                            bool isNew = String.IsNullOrEmpty(t.note.RelatedTask.Id);
                            requests.AddRange(GetSyncNoteRequest(t.note.RelatedTask, isNew));
                            t.note.Modified = false;
                        }
                    }

                    // Add deletes.
                    foreach (Task note in deletedNotes)
                    {
                        if (note != null && !String.IsNullOrEmpty(note.Id))
                            requests.Add(() => Program.Service.Tasks.Delete(taskList.Id, note.Id).Fetch());
                    }
                    deletedNotes.Clear();

                    // Execute all requests.
                    requests.ForEach(action => action());

                    // znovu načte data ze serveru
                    if (fetchNewData)
                    {
                        IList<Task> tasks = Program.Service.Tasks.List(taskList.Id).Fetch().Items;
                        tasksListView.BeginUpdate();
                        tasksListView.Items.Clear();
                        foreach (Task task in tasks)
                        {
                            AddNote(new NoteItem(task));
                        }
                        tasksListView.EndUpdate();
                    }
                }
                // je třeba se znovu autorizovat
                catch (Google.GoogleApiRequestException)
                {
                    tasksListView.EndUpdate();
                    Program.RefreshToken();
                    ClientSync(fetchNewData);
                }
                catch (DotNetOpenAuth.Messaging.ProtocolException)
                {
                    tasksListView.EndUpdate();
                    Console.WriteLine("Protocol exception captured");
                }
            }
        }

        private IEnumerable<Action> GetSyncNoteRequest(Task note, bool isNew)
        {
            var tasks = Program.Service.Tasks;

            if (isNew)
            {
                yield return () => note = tasks.Insert(note, taskList.Id).Fetch();
            }
            else
            {
                yield return
                    () =>
                    {
                        var req = tasks.Update(note, taskList.Id, note.Id);
                        note = req.Fetch();
                    };
            }
        }

        public void NoteForm_FormClosing()
        {
            ClientSync(false);
            Application.DoEvents();
            lock (sync) { }
        }
    }
}
